package edu.hawaii.ics111.exam;

//import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.InputMismatchException;
//import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;

/**
 * Shows exam questions and records their results.
 * 
 * @author Branden Ogata
 *
 */

public class ExamViewer
{
  /**
   * The frame to be displayed.
   * 
   */
  private JFrame frame;
  
  /**
   * The title of the exam.
   * 
   */
  private String title;

  /**
   * The menu bar for the frame.
   * 
   */
  private JMenuBar menuBar;
  
  /**
   * The File menu.
   * 
   */
  private JMenu file;
  
  /**
   * The About option.
   * 
   */
  private JMenuItem about;
  
  /**
   * The Exit option.
   * 
   */
  private JMenuItem exit;
  
  /**
   * The list of panels in which to display the questions.
   * 
   */
  private JPanel[] panels;
  
  /**
   * The list of ExamQuestion instances.
   * 
   */
  private ExamQuestion[] questions;

  /**
   * The currently displayed question.
   * 
   */
  private int currentQuestion;
    
  /**
   * The list of supplementary materials.
   * 
   */
  private ExamSupplement[] supplements;
  
  /**
   * Fills in the frame and shows the first question.
   * Currently uses the sample HTML exam questions;
   * the other constructor provides a way to send in questions.
   * 
   */
  
  @Deprecated
  public ExamViewer() 
  {
    // Let the other constructor handle everything aside from creating questions;
    // question creation is handled in getDefaultQuestions
    this(getDefaultQuestions(), "HTML Exam");    
  }

  /**
   * Initializes all pages of the exam and displays the first question.
   * Uses the parameter to create the list of questions.
   * 
   * @param questions An ExamQuestion[] that contains the list of questions for the exam.
   * @param title A String containing the title of the exam.
   * 
   */
  
  @Deprecated
  public ExamViewer(ExamQuestion[] questions, String title)
  {
    this.questions = new ExamQuestion[questions.length];
    System.arraycopy(questions, 0, this.questions, 0, this.questions.length);

    // Set up the frame
    this.title = title;
    this.frame = new JFrame(this.title + " (1 / " + this.questions.length + ")");
    this.frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    this.currentQuestion = 0;
    
    // Make a panel for each question
    this.panels = new JPanel[this.questions.length];
    
    // Set up button listener
    ButtonListener buttonListener = new ButtonListener();
    
    // For each panel
    for (int i = 0; i < this.panels.length; i++)
    {
//      System.out.println("Question " + this.questions[i].getNumber());
      this.panels[i] = new JPanel();
      this.panels[i].setName("questions " + (i + 1));
      this.panels[i].setLayout(new BoxLayout(this.panels[i], BoxLayout.PAGE_AXIS));
      this.panels[i].add(ExamViewer.getQuestionPanel(this.questions[i]));
      
      // If fill-in-the-blank
      if (this.questions[i].getCorrect() == -1)
      {
        this.panels[i].add(ExamViewer.getFillInBlankQuestion(this.questions[i]));
      }
      // Else multiple-choice
      else
      {
        this.panels[i].add(ExamViewer.getMultipleChoiceQuestion(this.questions[i]));
      }
      
      // Add buttons
      JPanel buttonPanel = new JPanel();
      buttonPanel.setName("buttonPanel");
      buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));

      // Add the Go To field and button
      JTextField toField = new JTextField(String.valueOf(i + 1), 4);
      toField.setName("goToField");
      toField.setFont(new Font(Font.SERIF,
                               toField.getFont().getStyle(),
                               toField.getFont().getSize()));
      JButton toButton = new JButton("View Question #");
      toButton.setName("goToButton");
      toButton.setFont(new Font(Font.SERIF,
                                toButton.getFont().getStyle(),
                                toButton.getFont().getSize()));
      toButton.addActionListener(buttonListener);
      toField.setMaximumSize(toButton.getPreferredSize());
      buttonPanel.add(toButton);
      buttonPanel.add(toField);
      
      buttonPanel.add(Box.createHorizontalGlue());
      
      // Add the Previous button
      JButton previous = new JButton("Previous");
      previous.setName("previous");
      previous.setFont(new Font(Font.SERIF,
                                previous.getFont().getStyle(),
                                previous.getFont().getSize()));      
      previous.addActionListener(buttonListener);
      buttonPanel.add(previous);

      // Add the Next button 
      JButton next = new JButton("Next");
      next.setName("next");
      next.setFont(new Font(Font.SERIF,
                            next.getFont().getStyle(),
                            next.getFont().getSize()));      
      next.addActionListener(buttonListener);
      buttonPanel.add(next);
      
      // If the first question, disable Previous button
      if (i == 0)
      {
        previous.setEnabled(false);
      }
      
      // If the last question, disable Next button
      if (i == this.questions.length - 1)
      {
        next.setEnabled(false);
      }
      
      // Insert space
      buttonPanel.add(Box.createHorizontalGlue());
      
      // In all cases, add the End Exam button
      JButton endExam = new JButton("End Exam");
      endExam.setName("endExam");
      endExam.setFont(new Font(Font.SERIF,
                               endExam.getFont().getStyle(),
                               endExam.getFont().getSize()));
      endExam.addActionListener(buttonListener);
      buttonPanel.add(endExam);
      
      // Add all of the above buttons to the panel
      this.panels[i].add(buttonPanel);
      
/*
      System.out.println("Panel Dimensions: " + this.panels[i].getPreferredSize());
      System.out.println("Panel Max Dimensions: " + this.panels[i].getMaximumSize());
*/
    }
    
    // Start with showing the first panel
    this.frame.add(this.panels[0]);
    this.frame.pack();
    this.frame.setVisible(true);  
  }
  
  /**
   * Initializes all pages of the exam and displays the first question.
   * Uses the parameter to create the list of questions.
   * This constructor allows grouping questions together.
   * 
   * @param items An ExamItem[] that contains the list of items on the exam.
   * @param title A String indicating the title of the exam.
   * 
   */
  
  public ExamViewer(ExamItem[] items, String title)
  { 
    // Sets the look and feel to match the system of the user
    try
    {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    // If any exception occurs, ignore for now; not sure how to test any of these cases
    catch (UnsupportedLookAndFeelException e)
    {
      // Do nothing
    }
    catch (ClassNotFoundException e)
    {
      // Do nothing      
    }
    catch (InstantiationException e)
    {
      // Do nothing      
    }
    catch (IllegalAccessException e)
    {
      // Do nothing      
    }
    
    // Temporary arrays to store the panels, questions, and supplementary materials
    JPanel[] tempPanels = new JPanel[items.length];
    ExamQuestion[] tempQuestions = new ExamQuestion[items.length];
    ExamSupplement[] tempSupplements = new ExamSupplement[items.length];
    Dimension panelSize = new Dimension(0, 0);
    int currentPanel = 0;
    int numQuestions = 0;
    int numSupplements = 0;
    
    // Set up the frame
    this.title = title;
    this.frame = new JFrame();
    this.frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    this.currentQuestion = 0;
    
    // Set up the menu
    MenuListener menuListener = new MenuListener();
    this.menuBar = new JMenuBar();
    this.file = new JMenu("File");
    this.about = new JMenuItem("About");
    this.about.addActionListener(menuListener);
    this.file.add(this.about);
    this.exit = new JMenuItem("Exit");
    this.exit.addActionListener(menuListener);
    this.file.add(this.exit);
    this.menuBar.add(this.file);
    this.frame.setJMenuBar(this.menuBar);
    
    // Set up button listener
    ButtonListener buttonListener = new ButtonListener();
    
    // For each item in the parameter
    for (int i = 0; i < items.length; i++)
    {
      tempPanels[currentPanel] = new JPanel();
      tempPanels[currentPanel].setLayout(new BoxLayout(tempPanels[currentPanel], 
                                                       BoxLayout.PAGE_AXIS));
      
      // Variable to determine which buttons to enable
      int initialItem = i;
      
      // Variables to store the question numbers in the panel
      int firstQuestion = -1;
      int lastQuestion = -1;
      
      // If the item is an instruction, prepare to create group of questions
      if (items[i] instanceof ExamInstruction)
      {
        tempPanels[currentPanel].add(ExamViewer.getInstructionPanel((ExamInstruction) items[i++]));
        
        // If the next item is the supplementary materials for the group of questions,
        // then add a button that opens that supplement in a separate panel
        if (items[i] instanceof ExamSupplement)
        {
          JPanel supplementPanel = new JPanel();
          supplementPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
          JButton supplement = new JButton(((ExamSupplement) items[i]).getTitle());
          supplement.setName("supplement" + numSupplements);
          supplement.addActionListener(buttonListener);
          supplementPanel.add(supplement);
          tempPanels[currentPanel].add(supplementPanel);
          tempSupplements[numSupplements++] = (ExamSupplement) items[i++];
        }
        
        // While the next item is a question 
        // and the group number of that question is greater than or equal to zero
        // (that is, while there are still questions left in the group)
        while ((i < items.length) && (items[i] instanceof ExamQuestion) && 
               (((ExamQuestion) items[i]).getGrouping() >= 0))
        {
          tempPanels[currentPanel].add(ExamViewer.getQuestionPanel((ExamQuestion) items[i]));
          tempQuestions[numQuestions] = (ExamQuestion) items[i];

          // If this is the first question, then assign the value to firstQuestion
          if (firstQuestion == -1)
          {
            firstQuestion = tempQuestions[numQuestions].getNumber();
          }
          
          // Always assign (or reassign) the value of lastQuestion
          lastQuestion = tempQuestions[numQuestions].getNumber();
          
          // If fill-in-the-blank
          if (tempQuestions[numQuestions].getCorrect() == -1)
          {
            tempPanels[currentPanel].add(ExamViewer.getFillInBlankQuestion(
                                           tempQuestions[numQuestions]));
          }
          // Else multiple-choice
          else
          {
            tempPanels[currentPanel].add(ExamViewer.getMultipleChoiceQuestion(
                                           tempQuestions[numQuestions]));
          }          
          
          // Increment to go on to the next element
          i++;
          numQuestions++;
        }
        
        // After exiting, go back one item so that the next iteration of the outer loop
        // will start from the next item rather than skipping it
        i--;
      }
      
      // Else treat as a single question and add one question
      else
      {
        // If the next item is some supplementary material, 
        // then display the supplement in the question itself
        if (items[i] instanceof ExamSupplement)
        {
          JTextArea supplement = new JTextArea(Math.min(((ExamSupplement) items[i]).getHeight(), 
                                                        32), 
                                               Math.min(((ExamSupplement) items[i]).getWidth(), 
                                                         64));
          supplement.setText(((ExamSupplement) items[i++]).getSupplement()); 
          JScrollPane supplementScroll = new JScrollPane(supplement);
          tempPanels[currentPanel].add(supplementScroll);
          numSupplements++;
        }
        
        // If the previous element was a supplement,
        // then add the question before the supplement
        // Else add the question to the panel
        // In both cases, the question is added at index 0
        tempPanels[currentPanel].add(ExamViewer.getQuestionPanel((ExamQuestion) items[i]), 0);
        tempQuestions[numQuestions] = (ExamQuestion) items[i];
        firstQuestion = tempQuestions[numQuestions].getNumber();
        lastQuestion = tempQuestions[numQuestions].getNumber();
        
        // If fill-in-the-blank
        if (tempQuestions[numQuestions].getCorrect() == -1)
        {
          // If there is more than one component in the panel, then a supplement is present,
          // so add a border between the supplement and the answers
          if (tempPanels[currentPanel].getComponents().length > 1)
          {
            JLabel answerLabel = new JLabel("Answer");
            answerLabel.setFont(new Font(Font.SERIF, 
                                         answerLabel.getFont().getStyle(),
                                         answerLabel.getFont().getSize() * 2));
            tempPanels[currentPanel].add(answerLabel);
          }
          
          tempPanels[currentPanel].add(ExamViewer.getFillInBlankQuestion(
                                         tempQuestions[numQuestions]));
        }
        // Else multiple-choice
        else
        {
          tempPanels[currentPanel].add(ExamViewer.getMultipleChoiceQuestion(
                                         tempQuestions[numQuestions]));
        }        
        numQuestions++;
      }
      
      // Add buttons
      JPanel buttonPanel = new JPanel();
      buttonPanel.setName("buttonPanel");
      buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));

      // Add the Go To field and button
      JButton toButton = new JButton("View Question #");
      toButton.setName("goToButton");
      toButton.setFont(new Font(Font.SERIF,
                                toButton.getFont().getStyle(),
                                toButton.getFont().getSize()));
      toButton.addActionListener(buttonListener);
      JTextField toField = new JTextField(String.valueOf(firstQuestion), 4);
      toField.setName("goToField");
      toField.setFont(new Font(Font.SERIF,
                               toField.getFont().getStyle(),
                               toField.getFont().getSize()));
      toField.setMaximumSize(toButton.getPreferredSize());
//      toButton.add(toField);
      buttonPanel.add(toButton);
      buttonPanel.add(toField);
      
      buttonPanel.add(Box.createHorizontalGlue());
      
      // Add the Previous button
      JButton previous = new JButton("Previous");
      previous.setName("previous");
      previous.setFont(new Font(Font.SERIF,
                                previous.getFont().getStyle(),
                                previous.getFont().getSize()));      
      previous.addActionListener(buttonListener);
      buttonPanel.add(previous);

      // Add the Next button 
      JButton next = new JButton("Next");
      next.setName("next");
      next.setFont(new Font(Font.SERIF,
                            next.getFont().getStyle(),
                            next.getFont().getSize()));      
      next.addActionListener(buttonListener);
      buttonPanel.add(next);
      
      // If the first question, disable Previous button
      if (initialItem == 0)
      {
        previous.setEnabled(false);
      }
      
      // If the last question, disable Next button
      if (i >= items.length - 1)
      {
        next.setEnabled(false);
      }
      
      // Insert space
      buttonPanel.add(Box.createHorizontalGlue());
      
      // In all cases, add the End Exam button
      JButton endExam = new JButton("End Exam");
      endExam.setName("endExam");
      endExam.setFont(new Font(Font.SERIF,
                               endExam.getFont().getStyle(),
                               endExam.getFont().getSize()));
      endExam.addActionListener(buttonListener);
      buttonPanel.add(endExam);
      
      // Add all of the above buttons to the panel
      tempPanels[currentPanel].add(buttonPanel);
      
      // If the x or y size of the current panel are greater than the current Dimension,
      // then adjust the values accordingly
      if (tempPanels[currentPanel].getPreferredSize().getWidth() > panelSize.getWidth())
      {
        panelSize.setSize(tempPanels[currentPanel].getPreferredSize().getWidth(), 
                          panelSize.getHeight()); 
      }
      if (tempPanels[currentPanel].getPreferredSize().getHeight() > panelSize.getHeight())
      {
        panelSize.setSize(panelSize.getWidth(), 
                          tempPanels[currentPanel].getPreferredSize().getHeight()); 
      }
      
      // Set the name of the panel; this will help in identifying the questions in the panel
      tempPanels[currentPanel++].setName("questions " + firstQuestion + "-" + lastQuestion);
    }
    
    // Once all of the items in the parameter are handled, 
    // assign panels, questions, and supplements 
    // to the instance arrays panels, questions, and supplements
    this.panels = ExamViewer.finalizePages(tempPanels, currentPanel, panelSize);
//    this.panels = new JPanel[currentPanel];
//    System.arraycopy(tempPanels, 0, this.panels, 0, this.panels.length);
    this.questions = new ExamQuestion[numQuestions];
    System.arraycopy(tempQuestions, 0, this.questions, 0, this.questions.length);
    this.supplements = new ExamSupplement[numSupplements];
    System.arraycopy(tempSupplements, 0, this.supplements, 0, this.supplements.length);
    
    // Start with showing the first panel
    this.frame.setTitle(this.title + " (" + ExamViewer.getRange(this.panels[0]) + " of " + 
                        this.questions.length + ")");
    this.frame.add(this.panels[0]);
    this.frame.pack();
    this.frame.setVisible(true);  
  }  
  
  /**
   * Prepares the default list of HTML questions for the exam.
   * 
   * @return htmlQuestions An ExamQuestion[] containing the default list of HTML questions.
   * 
   */
  
  private static final ExamQuestion[] getDefaultQuestions()
  {
    // Set up the questions first
    ExamQuestion[] htmlQuestions = new ExamQuestion[10];
    
    // Question 1
    htmlQuestions[0] = new ExamQuestion(1, "What does HTML stand for?", 
                                        new String[] {"HyperText Markup Language",
                                                      "High-Tech Markup Language",
                                                      "HyperText Markup Linguistics",
                                                      "Lower taxes and a balanced budget"}, 
                                        0);
    
    // Question 2
    htmlQuestions[1] = new ExamQuestion(2, "What is the HTML tag for the largest heading?", 
                                        new String[] {"<h1>"}, 
                                        -1);
    
    // Question 3
    htmlQuestions[2] = new ExamQuestion(3, "What is the HTML to insert a line break?", 
                                        new String[] {"<br />",
                                                      "<br/>"}, 
                                        -1);
    
    // Question 4
    htmlQuestions[3] = new ExamQuestion(4, "Which of the below correctly sets red as " + 
                                           "the background color of an element?", 
                                        new String[] {"background-color=\"red\"",
                                                      "style=\"background-color=\"red\"\"", 
                                                      "style=\"background-color:red\"",
                                                      "style=\"backgroundcolor=\"red\"\""}, 
                                        2);    
    
    // Question 5
    htmlQuestions[4] = new ExamQuestion(5, "Which of the below correctly makes a hyperlink " + 
                                           "to myuh.hawaii.edu?", 
                                        new String[] {"<a href=\"MyUH\">myuh.hawaii.edu</a>",
                                                      "<link target=\"myuh.hawaii.edu\">MyUH</a>", 
                                                      "<a target=\"myuh.hawaii.edu\">MyUH</a>",
                                                      "<a href=\"myuh.hawaii.edu\">MyUH</a>"}, 
                                        3);        
    
    // Question 6
    htmlQuestions[5] = new ExamQuestion(6, "How do you open a link to www.ics.hawaii.edu " + 
                                           "in a new window?", 
                                        new String[] {"<a href=\"www.ics.hawaii.edu\">" + 
                                                        "ICS Department</a>",
                                                      "<a href=\"www.ics.hawaii.edu\" " + 
                                                         "target=\"_blank\">ICS Department</a>", 
                                                      "<a href=\"www.ics.hawaii.edu\" " + 
                                                         "target=\"_frame\">ICS Department</a>",
                                                      "Very carefully"}, 
                                        1);        
    
    // Question 7
    htmlQuestions[6] = new ExamQuestion(7, "How can you make a list with numbered items?", 
                                        new String[] {"Use <nl> for the list and " + 
                                                        "<li> for the items",
                                                      "Use <ul> for the list and " + 
                                                        "<li> for the items",
                                                      "Use <li> for the list and " + 
                                                        "<ol> for the items",
                                                      "Use <ol> for the list and " + 
                                                        "<li> for the items"}, 
                                        3);            
    
    // Question 8
    htmlQuestions[7] = new ExamQuestion(8, "Which of the following is the correct HTML " + 
                                           "to create a checkbox?", 
                                        new String[] {"<form><input type=\"checkbox\" /></form>",
                                                      "<input type=\"checkbox\" />",
                                                      "<form><input type=\"check\" /></form>",
                                                      "<form><input checked=\"true\" /></form>"}, 
                                        0);
    
    // Question 9
    htmlQuestions[8] = new ExamQuestion(9, "Which of the below is the correct HTML " + 
                                           "to make a text input field?", 
                                        new String[] {"<input type=\"text\" />",
                                                      "<form><input type=\"textarea\" /></form>",
                                                      "<form><input type=\"text\" /></form>",
                                                      "<form><input type=text /></form>"}, 
                                        2);
    
    // Question 10
    htmlQuestions[9] = new ExamQuestion(10, "Which of the tags below will create a " + 
                                            "drop-down list in a form?", 
                                        new String[] {"<ul> for the list, <li> for the choices",
                                                      "<select> for the list, " + 
                                                        "<option> for the choices",
                                                      "<select> for the list, " + 
                                                        "<choice> for the choices",
                                                      "<list> for the list, " +
                                                        "<option> for the choices"}, 
                                        1);
    
    return htmlQuestions;
  }
  
  /**
   * Returns a JPanel containing the text of a question.
   * 
   * @param question The ExamQuestion to get data from.
   * 
   * @return questionPanel A JPanel containing the formatted question.
   * 
   */
  
  private static final JPanel getQuestionPanel(ExamQuestion question)
  {
    JPanel questionPanel = new JPanel();
    questionPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
    JLabel questionLabel = new JLabel("<html><body>" + question.getNumber() + ": " + 
                                      question.getQuestion() + "</body></html>"); 
    questionLabel.setFont(new Font(Font.SERIF, 
                                   questionLabel.getFont().getStyle(),
                                   questionLabel.getFont().getSize() * 2));
    questionPanel.add(questionLabel);
    return questionPanel;    
  }
  
  /**
   * Returns a JTextField in which the user may answer.
   * 
   * @param question The ExamQuestion to get data from.
   * 
   * @return answerField The JTextField for the user to type an answer in.
   * 
   */
  
  private static final JTextField getFillInBlankQuestion(ExamQuestion question)
  {
    JTextField answerField = new JTextField(32);
    answerField.setName("typeInAnswer" + question.getNumber());
    answerField.setFont(new Font(Font.SERIF,
                                  answerField.getFont().getStyle(),
                                  (int) (answerField.getFont().getSize() * 1.28)));
    return answerField;
  }
  
  /**
   * Returns a JPanel containing radio buttons for user answer selection.
   * 
   * @param question The ExamQuestion containing the question and answers to use.
   * 
   * @return answerPanel The JPanel containing the valid selections and their labels.
   * 
   */
  
  private static final JPanel getMultipleChoiceQuestion(ExamQuestion question)
  {
    JPanel answerPanel = new JPanel();
    answerPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
    answerPanel.setName("bubbleInAnswer" + question.getNumber());
    JPanel choicePanel = new JPanel();
    choicePanel.setLayout(new BoxLayout(choicePanel, BoxLayout.PAGE_AXIS));
    choicePanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
    ButtonGroup buttonGroup = new ButtonGroup(); 
    
    for (int j = 0; j < question.getAnswers().length; j++)
    {
      JRadioButton nextOption = new JRadioButton(question.getAnswers()[j]);
      nextOption.setName("bubbleInAnswer" + question.getNumber() + "-" + j);
      nextOption.setFont(new Font(Font.SERIF,
                                  nextOption.getFont().getStyle(),
                                  (int) (nextOption.getFont().getSize() * 1.28)));
      choicePanel.add(nextOption);
      buttonGroup.add(nextOption);
      
/*
      // If necessary, increase the size of answerPanel to fit the new radio button
      System.out.println("Answer Panel Dimensions: " + answerPanel.getPreferredSize());
      System.out.println("Answer Panel Max Dimensions: " + answerPanel.getMaximumSize());
      System.out.println("Next Option Dimensions: " + nextOption.getPreferredSize());
      System.out.println("Next Option Max Dimensions: " + nextOption.getMaximumSize());
*/
    }

    // Increase the size of the answer panel to ensure that options are not truncated
    Dimension twiceWidth = new Dimension((int) choicePanel.getPreferredSize().getWidth() * 2, 
                                         (int) choicePanel.getPreferredSize().getHeight());
    choicePanel.setPreferredSize(twiceWidth);
    choicePanel.setMaximumSize(twiceWidth);

    answerPanel.add(choicePanel);

/*    
    answerPanel.setBorder(BorderFactory.createCompoundBorder(
    BorderFactory.createLineBorder(Color.red),
    answerPanel.getBorder())); 
*/
    
    return answerPanel;
  }
  
  /**
   * Returns a JPanel containing the informational text for the section.
   * 
   * @param instruction The ExamInstruction to get data from.
   * 
   * @return instructionPanel A JPanel containing the formatted instructions.
   * 
   */
  
  private static final JPanel getInstructionPanel(ExamInstruction instruction)
  {
    JPanel instructionPanel = new JPanel();
    instructionPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
    JLabel instructionLabel = new JLabel("<html><body>" + instruction.getInstruction() + 
                                         "</body></html>"); 
    instructionLabel.setFont(new Font(Font.SERIF, 
                                      instructionLabel.getFont().getStyle(),
                                      instructionLabel.getFont().getSize() * 2));
    instructionPanel.add(instructionLabel);
    return instructionPanel;    
  }
  
  /**
   * Returns what the title of the frame should be given the current panel.
   * 
   * @param panel The JPanel that the frame is about to display.
   * 
   * @return questionRange A String containing the value to be written as the range of the frame.
   * 
   */
  
  private static final String getRange(JPanel panel)
  {
    String questionRange = "";
    int startQuestion = 0;
    int endQuestion = 0;
    
    // Get the name of the panel and extract the numbers in the name
    try
    {
      if (panel.getName() != null)
      {
        String range = panel.getName().split(" ")[1];
        
        // If there is a "-", then the range is between two values
        if (range.contains("-"))
        {
          startQuestion = Integer.parseInt(range.split("-")[0]);
          endQuestion = Integer.parseInt(range.split("-")[1]);
        }
        // Else there is only one question in the panel and the range is a single value
        else
        {
          startQuestion = Integer.parseInt(range);
          endQuestion = Integer.parseInt(range);
        }
      }
    }
    catch (ArrayIndexOutOfBoundsException e)
    {
      System.out.println("Array Index Error");
      // Do nothing
    }
    
    questionRange += "Question" + ((startQuestion == endQuestion) ? 
                                   (" " + startQuestion) : 
                                   ("s " + startQuestion + "-" + endQuestion));
    
    return questionRange;
  }
  
  /**
   * Returns the index of the JPanel in this.panels that contains the parameter question number.
   * 
   * @param questionNumber The int containing the number of the question to look for.
   * 
   * @return index An int containing the index of the JPanel that contains the parameter number.
   *               If not found, then -1.
   * 
   */
  
  private final int getIndexOfQuestion(int questionNumber)
  {
    int index = -1;
    int count = 0;
    int rangeStart = 0;
    int rangeEnd = 0;
        
    // Go through each panel in this.panels until reaching the end or finding the question
    while ((count < this.panels.length) && (index == -1))
    {
      try
      {
        // If the question number is within the range given in the JPanel title,
        // then the question is in that JPanel
        String range = ExamViewer.getRange(this.panels[count]);        
        range = range.split(" ")[1];
        
        // If the range contains "-", then take the two values
        if (range.contains("-"))
        {
          rangeStart = Integer.parseInt(range.split("-")[0]);
          rangeEnd = Integer.parseInt(range.split("-")[1]);
        }
        else
        {
          rangeStart = Integer.parseInt(range);
          rangeEnd = Integer.parseInt(range);
        }
        
        if ((rangeStart <= questionNumber) && (questionNumber <= rangeEnd))
        {
          index = count;
        }
      }
      catch (ArrayIndexOutOfBoundsException e)
      {
        // Do nothing
      }
      
      count++;
    }
    
    return index;
  }
  
  /**
   * Converts the temporary exam pages to the actual pages for presentation.
   * In addition to having the correct array size, the finalized panels should have 
   * identical dimensions and corrected layouts, should any of the latter had problems earlier.
   * 
   * @param tempPanels The JPanel[] containing the temporary panels.
   * @param numPanels The int indicating the requisite number of panels.
   * @param size The Dimension indicating the size to create the panels with.
   * 
   * @return finalPanels A JPanel[] containing the finalized panels for the exam.
   * 
   */
  
  private static final JPanel[] finalizePages(JPanel[] tempPanels, int numPanels, Dimension size)
  {
    JPanel[] finalPanels = new JPanel[numPanels];
    
    // For each panel to add
    for (int i = 0; i < numPanels; i++)
    {
      finalPanels[i] = new JPanel();
      finalPanels[i].setLayout(new BoxLayout(finalPanels[i], 
                               BoxLayout.PAGE_AXIS));
      finalPanels[i].setName(tempPanels[i].getName());

      // Resize the current panel such that all panels will have the same size
      finalPanels[i].setPreferredSize(size);
      
      // Add each component in the temporary panel to the current panel
      for (Component c : tempPanels[i].getComponents())
      {
        finalPanels[i].add(c);
      }
    }
    
    return finalPanels;
  }
  
  /**
   * Listens for any button presses.
   * 
   */
  
  private class ButtonListener implements ActionListener
  {
    /**
     * Handles any button presses that occur.
     * 
     * @param event An ActionEvent object that recorded the event.
     * 
     */
    
    @Override
    public void actionPerformed(ActionEvent event) 
    {
      // If the source was a button
      if (event.getSource() instanceof JButton)
      {
        // Store the current size of the frame
        Dimension currentSize = frame.getSize();
        
        // If the source was a Supplement button
        if (((JButton) event.getSource()).getName().contains("supplement"))
        {
          try
          {
            // Get the index of the specific supplement
            int supplementNumber = 
              Integer.parseInt(((JButton) event.getSource()).getName().replaceAll("\\D", ""));

            // Place the supplementary material inside the text area
            JTextArea supplementText = new JTextArea(32, 64);
            supplementText.setFont(new Font(Font.MONOSPACED,
                                            supplementText.getFont().getStyle(),
                                            supplementText.getFont().getSize()));            
            supplementText.setText(supplements[supplementNumber].getSupplement());
            supplementText.setEditable(false);
            JScrollPane supplementScroll = new JScrollPane(supplementText);
            
            // Show the supplementary material as a non-modal dialog
            JDialog supplementDisplay = new JDialog(frame, 
                                                    supplements[supplementNumber].getTitle(),
                                                    false);
            supplementDisplay.add(supplementScroll);
            supplementDisplay.pack();
            supplementDisplay.setVisible(true);
          }
          catch (NumberFormatException e)
          {
            // Indicate that the supplement could not be found
          }
        }
        // Else if the source was the Next button
        else if (((JButton) event.getSource()).getName().equals("next"))
        {
          int questionNumber = currentQuestion + 1;
          
          panels[getIndexOfQuestion(questionNumber)].setVisible(false);
          frame.remove(panels[getIndexOfQuestion(questionNumber)]);       
          panels[getIndexOfQuestion(questionNumber) + 1].setSize(currentSize);
          panels[getIndexOfQuestion(questionNumber) + 1].setVisible(true);
          frame.add(panels[getIndexOfQuestion(questionNumber) + 1]);
          frame.setTitle(title + " (" + getRange(panels[getIndexOfQuestion(questionNumber) + 1]) + 
                         " of " + questions.length + ")");
          
          if (panels[getIndexOfQuestion(questionNumber) + 1].getName() != null)
          {
            String nextPanelName = panels[getIndexOfQuestion(questionNumber) + 1].getName();
            String nextPanelRange = nextPanelName.replace("questions ", "");
            
            try
            {
              currentQuestion = Integer.parseInt(nextPanelRange.split("-")[0]) - 1;
            }
            catch (InputMismatchException e)
            {
              // Do nothing
            }
            catch (ArrayIndexOutOfBoundsException e)
            {
              // Do nothing
            }
          }
          
//          frame.pack();
          frame.setVisible(true);
        }
        // Else if the source was the Previous button
        else if (((JButton) event.getSource()).getName().equals("previous"))
        {
          int questionNumber = currentQuestion + 1;
          panels[getIndexOfQuestion(questionNumber)].setVisible(false);
          frame.remove(panels[getIndexOfQuestion(questionNumber)]);
          panels[getIndexOfQuestion(questionNumber) - 1].setSize(currentSize);
          panels[getIndexOfQuestion(questionNumber) - 1].setVisible(true);
          frame.add(panels[getIndexOfQuestion(questionNumber) - 1]);
          frame.setTitle(title + " (" + getRange(panels[getIndexOfQuestion(questionNumber) - 1]) + 
                         " of " + questions.length + ")");
          
          if (panels[getIndexOfQuestion(questionNumber) - 1].getName() != null)
          {
            String nextPanelName = panels[getIndexOfQuestion(questionNumber) - 1].getName();
            String nextPanelRange = nextPanelName.replace("questions ", "");
            
            try
            {
              currentQuestion = Integer.parseInt(nextPanelRange.split("-")[0]) - 1;
            }
            catch (InputMismatchException e)
            {
              // Do nothing
            }
            catch (ArrayIndexOutOfBoundsException e)
            {
              // Do nothing
            }
          }          
          
//          frame.pack();
          frame.setVisible(true);
        }
        // Else if the source was the View button
        else if (((JButton) event.getSource()).getName().equals("goToButton"))
        {
          JTextField goToField = null;
          
          try
          {
            int nextQuestion = currentQuestion;
            int currentPanel = getIndexOfQuestion(currentQuestion + 1);            
            
            // Find the panel holding the buttons
            for (int i = 0; i < panels[currentPanel].getComponentCount(); i++)
            {
              if (panels[currentPanel].getComponent(i) instanceof JPanel)
              {
                JPanel panel = (JPanel) panels[currentPanel].getComponent(i);
                
                // Once the button panel is found, get the text field
                if ("buttonPanel".equals(panel.getName()))
                {
                  for (int j = 0; j < panel.getComponentCount(); j++)
                  {
                    if (panel.getComponent(j) instanceof JTextField)
                    {
                      goToField = (JTextField) panel.getComponent(j);
                      nextQuestion = Integer.valueOf(goToField.getText());
                      goToField.setText(String.valueOf(currentQuestion + 1));
                    }
                  }
                }
              }
            }
            
            panels[currentPanel].setVisible(false);
            frame.remove(panels[currentPanel]);
            currentQuestion = nextQuestion - 1;
            
            // Verify that the question number is valid
            // If less than 0, go to panels[0]
            if (currentQuestion < 0)
            {
              currentQuestion = 0;
            }
            // If greater than the last question, go to the last question
            else if (currentQuestion >= questions.length)
            {
              currentQuestion = questions.length - 1;
            }

            currentPanel = 0;
            int foundPanelNumber = getIndexOfQuestion(currentQuestion + 1);
                        
            // Show the appropriate panel
            panels[foundPanelNumber].setSize(currentSize);
            panels[foundPanelNumber].setVisible(true);
            frame.add(panels[foundPanelNumber]);
            frame.setTitle(title + " (" + getRange(panels[foundPanelNumber]) + " of " + 
                           questions.length + ")");
//            frame.pack();
            frame.setVisible(true);
            
/*            
            panels[currentQuestion].setVisible(true);
            frame.add(panels[currentQuestion]);
            frame.setTitle("HTML Exam (" + (currentQuestion + 1) + " / " + questions.length + ")");
            frame.pack();
            frame.setVisible(true);          
*/
          }
          catch (InputMismatchException e)
          {
            if (goToField != null)
            {
              goToField.setText(String.valueOf(currentQuestion + 1));
            }
          }
          catch (NumberFormatException e)
          {
            if (goToField != null)
            {
              goToField.setText(String.valueOf(currentQuestion + 1));
            }            
          }
        }
        // Else if the source was the End Exam button
        else if (((JButton) event.getSource()).getName().equals("endExam"))
        {
          // If the user confirms the decision to end
          if (JOptionPane.showConfirmDialog(null, "Do you want to end the exam?", "Confirm Exit",
                                            JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION)
          {
            // Go through each panel and determine if the answer was correct
            //   Note that this must check each question in the panel, 
            //   so loop through the components
            // If incorrect, add to the text area for review
            JTextArea reviewText = new JTextArea(8, 32);
            reviewText.setFont(new Font(Font.SERIF,
                                        reviewText.getFont().getStyle(),
                                        reviewText.getFont().getSize()));
            int correctAnswers = 0;
            int currentNumber = 0;
            
            for (int i = 0; i < panels.length; i++)
            {
              // Go through each component in the panel to find questions
              for (Component c : panels[i].getComponents())
              {
                // If the component has a name and that name contains the current question number
                if ((c.getName() != null) && 
                    (c.getName().contains(String.valueOf(currentNumber + 1))))
                {
                  // If fill-in-the-blank
                  if (c instanceof JTextField)
                  {
                    JTextField userAnswerField = (JTextField) c;
                    String userAnswer = userAnswerField.getText();
                    
                    // If the answer is correct, increment the number of correct answers
                    if (questions[currentNumber].isCorrect(userAnswer))
                    {
                      correctAnswers++;
                    }
                    // Else print the question, the given answer, and the correct answer
                    else
                    {
                      String question = questions[currentNumber].getQuestion();
                      question = question.replaceAll("<br />", "");
                      question = question.replaceAll("<code>", "");
                      question = question.replaceAll("</code>", "");
                      question = question.replaceAll("&#60", "<");
                      question = question.replaceAll("&#62", ">");
                      reviewText.append("Question " + (currentNumber + 1) + ": " + 
                                        question + "\n");
                      reviewText.append("  Answer Given: " + userAnswer + "\n");
                      reviewText.append("  Correct Answer: " + 
                                        questions[currentNumber].getCorrectAnswer() + "\n");
                      reviewText.append("\n");
                    }
                    
                    currentNumber++;                    
                  }
                  // Else if multiple-choice
                  else if (c instanceof JPanel)
                  {
                    JRadioButton[] bubbleOptions = 
                      new JRadioButton[questions[currentNumber].getAnswers().length];
                    int currentRadioButton = 0;
                    boolean foundSelected = false;
                      
                    JPanel panel = (JPanel) c;
                    
                    // Go one panel further
                    for (Component buttonPanel : panel.getComponents())
                    {
                      if (buttonPanel instanceof JPanel)
                      {
                        for (Component radioButton : ((JPanel) buttonPanel).getComponents())
                        {
                          // If the component is in fact a radio button
                          if (radioButton instanceof JRadioButton)
                          {
                            bubbleOptions[currentRadioButton] = (JRadioButton) radioButton;
                            currentRadioButton++;                    
                          }
                        }
                      }
                    }
                    
                    // Of those radio buttons, find the one that is selected
                    for (int j = 0; j < bubbleOptions.length; j++)
                    {
                      if (bubbleOptions[j].isSelected())
                      {
                        foundSelected = true;
                          
                        // If the selected one corresponds to the correct answer,
                        // increment the number of correct answers
                        if (j == questions[currentNumber].getCorrect())
                        {
                          correctAnswers++;
                        }
                        // Else print the question, the given answer, and the correct answer
                        else
                        {
                          String question = questions[currentNumber].getQuestion();
                          question = question.replaceAll("<br />", "");
                          question = question.replaceAll("<code>", "");
                          question = question.replaceAll("</code>", "");
                          question = question.replaceAll("&#60", "<");
                          question = question.replaceAll("&#62", ">");
                          reviewText.append("Question " + (currentNumber + 1) + ": " + 
                                            question + "\n");
                          reviewText.append("  Answer Given: " + 
                                            questions[currentNumber].getAnswers()[j] + "\n");
                          reviewText.append("  Correct Answer: " + 
                                            questions[currentNumber].getCorrectAnswer() + "\n");
                          reviewText.append("\n");
                        }
                      }
                    }
                      
                    // If none of the radio buttons were selected, 
                    // print out the question, the (blank) given answer, and the correct answer
                    if (!foundSelected)
                    {
                      String question = questions[currentNumber].getQuestion();
                      question = question.replaceAll("<br />", "");
                      question = question.replaceAll("<code>", "");
                      question = question.replaceAll("</code>", "");
                      question = question.replaceAll("&#60", "<");
                      question = question.replaceAll("&#62", ">");
                      reviewText.append("Question " + (currentNumber + 1) + ": " + question + "\n");
                      reviewText.append("  Answer Given: \n");
                      reviewText.append("  Correct Answer: " + 
                                        questions[currentNumber].getCorrectAnswer() + "\n");
                      reviewText.append("\n");                  
                    }               
                    
                    currentNumber++;
                  }
                }
              }
            }
            
// The code in the below comment is from a previous version and has been kept for reference
/*
            {                            
              // If the correct answer is given as -1, then assume that it is a fill-in-the-blank
              // and that the first answer listed in the answers array is correct
              // In addition, if the answers array only has one answer anyway, 
              // then it is a fill-in-the-blank
              if ((questions[currentQuestion].getCorrect() == -1) || 
                  (questions[currentQuestion].getAnswers().length == 1))
              {
                // Go through each component in the panel to find the JTextField
                for (Component c : panels[i].getComponents())
                {
                  if (c instanceof JTextField)
                  {
                    JTextField userAnswerField = (JTextField) c;
                    String userAnswer = userAnswerField.getText();
                    
                    // If the answer is correct, increment the number of correct answers
                    if (questions[currentQuestion].isCorrect(userAnswer))
                    {
                      correctAnswers++;
                    }
                    // Else print the question, the given answer, and the correct answer
                    else
                    {
                      reviewText.append("Question " + (currentQuestion + 1) + ": " + 
                                        questions[i].getQuestion() + "\n");
                      reviewText.append("  Answer Given: " + userAnswer + "\n");
                      reviewText.append("  Correct Answer: " + 
                                        questions[currentQuestion].getCorrectAnswer() + "\n");
                      reviewText.append("\n");
                    }
                    
                    currentQuestion++;
                  }
                }
              }
              // Else assume that it is a multiple-choice question
              else
              {
                JRadioButton[] bubbleOptions = 
                  new JRadioButton[questions[currentQuestion].getAnswers().length];
                int currentRadioButton = 0;
                boolean foundSelected = false;
                
                // Go through each component to find all JRadioButton instances
                for (Component c : panels[i].getComponents())
                {
                  // If the component is a panel containing JRadioButtons
                  // and refers to the correct question
                  if ((c instanceof JPanel) && 
                      ((c.getName() != null) && 
                       (c.getName().equals("bubbleInAnswer" + (currentQuestion + 1)))))
                  {
                    JPanel panel = (JPanel) c;
                    
                    for (Component radioButton : panel.getComponents())
                    {
                      // If the component is in fact a radio button
                      if (radioButton instanceof JRadioButton)
                      {
                        bubbleOptions[currentRadioButton] = (JRadioButton) radioButton;
                        currentRadioButton++;                    
                      }
                    }
                  }
                }
                
                // Of those radio buttons, find the one that is selected
                for (int j = 0; j < bubbleOptions.length; j++)
                {
                  if (bubbleOptions[j].isSelected())
                  {
                    foundSelected = true;
                    
                    // If the selected one corresponds to the correct answer,
                    // increment the number of correct answers
                    if (j == questions[currentQuestion].getCorrect())
                    {
                      correctAnswers++;
                    }
                    // Else print the question, the given answer, and the correct answer
                    else
                    {
                      reviewText.append("Question " + (currentQuestion + 1) + ": " + 
                                        questions[i].getQuestion() + "\n");
                      reviewText.append("  Answer Given: " + 
                                        questions[currentQuestion].getAnswers()[j] + "\n");
                      reviewText.append("  Correct Answer: " + 
                                        questions[currentQuestion].getCorrectAnswer() + "\n");
                      reviewText.append("\n");
                    }
                  }
                }
                
                // If none of the radio buttons were selected, 
                // print out the question, the (blank) given answer, and the correct answer
                if (!foundSelected)
                {
                  reviewText.append("Question " + (currentQuestion + 1) + ": " + 
                                    questions[currentQuestion].getQuestion() + "\n");
                  reviewText.append("  Answer Given: \n");
                  reviewText.append("  Correct Answer: " + 
                                    questions[currentQuestion].getCorrectAnswer() + "\n");
                  reviewText.append("\n");                  
                }
              }
            }
*/
              
            // Empty the frame and print the results
            frame.setTitle(title + " Results");
            frame.remove(panels[getIndexOfQuestion(currentQuestion + 1)]);
            JPanel panelResults = new JPanel();
            panelResults.setLayout(new BoxLayout(panelResults, BoxLayout.PAGE_AXIS));
            panelResults.setPreferredSize(panels[getIndexOfQuestion(
                                                   currentQuestion + 1)].getPreferredSize());
            JLabel results = new JLabel("You got " + correctAnswers + 
                                        " correct out of " + questions.length + " question" + 
                                        ((questions.length == 1) ? ("") : ("s")) + ".");
            results.setFont(new Font(Font.SERIF,
                                     results.getFont().getStyle(),
                                     results.getFont().getSize() * 2));
            panelResults.add(results);
            
            // Add review of incorrect questions here
            JScrollPane reviewScrollPane = new JScrollPane(reviewText);
            reviewScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            reviewText.setLineWrap(true);
            reviewText.setWrapStyleWord(true);
            reviewText.setEditable(false);
            panelResults.add(reviewScrollPane);
            
            frame.add(panelResults);
            frame.pack();
            frame.setVisible(true);          
          }
        }
      }  
    }  
  }
  
  /**
   * Listens for any menu selections.
   * 
   */
  
  private class MenuListener implements ActionListener
  {
    /**
     * Handles any menu selections that do occur.
     * 
     * @param event The ActionEvent event that recorded the menu selection.
     * 
     */
    
    public void actionPerformed(ActionEvent event)
    {
      // If About was the source, then display dialog about the developer and assistants
      if (event.getSource() == about)
      {
        String output = "ICS 111 Final Exam Practice\n";
        output += "\u00A9 2012 Branden Ogata\n";
        output += "\n";
        output += "Special thanks to:\n";
        output += "<html><body><ul>";
        output += "  <li>Sean Mosier and Kristen Nakasone for their work "; 
        output += "in testing the application</li>";
        output += "  <li>BJ DeLaCruz and Pauline Wu for their advice regarding ";
        output += "the design and layout of the application</li>";
        output += "  <li>Doctors Martha Crosby and Michael Brian Ogawa for giving this ";
        output += "developer the opportunity to serve as a lab assistant in ICS 111</li>";
        output += "  <li>Doctor Scott Robertson and Robert Ward for generally ";
        output += "tolerating the existence of this developer</li>";
        output += "</ul></body></html>";
        output += "\n";
        
        JOptionPane.showMessageDialog(null, output, "About the Application", 
                                      JOptionPane.INFORMATION_MESSAGE);
      }
      
      // Else if Exit was the source, then close the frame
      else if (event.getSource() == exit)
      {
        frame.dispose();
      }
    }
  }
}